package com.chatplus.application.common.config;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.tags.Tag;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Swagger 文档配置
 *
 * @author Lion Li
 */
@Configuration
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
public class SpringDocConfig {

    @Bean
    @ConfigurationProperties(prefix = "springdoc")
    public SpringDocProperties springDocProperties() {
        return new SpringDocProperties();
    }

    @Bean
    @ConditionalOnMissingBean(OpenAPI.class)
    public OpenAPI openApi(SpringDocProperties properties) {
        OpenAPI openApi = new OpenAPI();
        // 文档基本信息
        SpringDocProperties.InfoProperties infoProperties = properties.getInfo();
        Info info = convertInfo(infoProperties);
        openApi.info(info);
        // 扩展文档信息
        openApi.externalDocs(properties.getExternalDocs());
        openApi.tags(properties.getTags());
        openApi.paths(properties.getPaths());
        openApi.components(properties.getComponents());
        Set<String> keySet = properties.getComponents().getSecuritySchemes().keySet();
        List<SecurityRequirement> list = new ArrayList<>();
        SecurityRequirement securityRequirement = new SecurityRequirement();
        keySet.forEach(securityRequirement::addList);
        list.add(securityRequirement);
        openApi.security(list);

        return openApi;
    }

    private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) {
        Info info = new Info();
        info.setTitle(infoProperties.getTitle());
        info.setDescription(infoProperties.getDescription());
        info.setContact(infoProperties.getContact());
        info.setLicense(infoProperties.getLicense());
        info.setVersion(infoProperties.getVersion());
        return info;
    }


    /**
     * 单独使用一个类便于判断 解决springdoc路径拼接重复问题
     *
     * @author Lion Li
     */
    static class PlusPaths extends Paths {

        public PlusPaths() {
            super();
        }
    }

    public static class SpringDocProperties {

        /**
         * 文档基本信息
         */
        @NestedConfigurationProperty
        private InfoProperties info = new InfoProperties();

        /**
         * 扩展文档地址
         */
        @NestedConfigurationProperty
        private ExternalDocumentation externalDocs;

        /**
         * 标签
         */
        private List<Tag> tags = null;

        /**
         * 路径
         */
        @NestedConfigurationProperty
        private Paths paths = null;

        /**
         * 组件
         */
        @NestedConfigurationProperty
        private Components components = null;

        /**
         * <p>
         * 文档的基础属性信息
         * </p>
         *
         * @see io.swagger.v3.oas.models.info.Info
         * <p>
         * 为了 springboot 自动生产配置提示信息，所以这里复制一个类出来
         */
        public static class InfoProperties {

            /**
             * 标题
             */
            private String title = null;

            /**
             * 描述
             */
            private String description = null;

            /**
             * 联系人信息
             */
            @NestedConfigurationProperty
            private Contact contact = null;

            /**
             * 许可证
             */
            @NestedConfigurationProperty
            private License license = null;

            /**
             * 版本
             */
            private String version = null;

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }

            public String getDescription() {
                return description;
            }

            public void setDescription(String description) {
                this.description = description;
            }

            public Contact getContact() {
                return contact;
            }

            public void setContact(Contact contact) {
                this.contact = contact;
            }

            public License getLicense() {
                return license;
            }

            public void setLicense(License license) {
                this.license = license;
            }

            public String getVersion() {
                return version;
            }

            public void setVersion(String version) {
                this.version = version;
            }
        }

        public InfoProperties getInfo() {
            return info;
        }

        public void setInfo(InfoProperties info) {
            this.info = info;
        }

        public ExternalDocumentation getExternalDocs() {
            return externalDocs;
        }

        public void setExternalDocs(ExternalDocumentation externalDocs) {
            this.externalDocs = externalDocs;
        }

        public List<Tag> getTags() {
            return tags;
        }

        public void setTags(List<Tag> tags) {
            this.tags = tags;
        }

        public Paths getPaths() {
            return paths;
        }

        public void setPaths(Paths paths) {
            this.paths = paths;
        }

        public Components getComponents() {
            return components;
        }

        public void setComponents(Components components) {
            this.components = components;
        }
    }
}
